package com.iflytek.jzcpx.procuracy.web.cont.bd.impl;

import javax.annotation.Resource;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.CountDownLatch;
import java.util.stream.Collectors;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.iflytek.jzcpx.procuracy.common.model.HandContResult;
import com.iflytek.jzcpx.procuracy.common.model.HandContResultInfo;
import com.iflytek.jzcpx.procuracy.common.model.HandContResultInfoJzml;
import com.iflytek.jzcpx.procuracy.common.model.HandContResultInfoJzmlwj;
import com.iflytek.jzcpx.procuracy.cont.common.util.ContUtil;
import com.iflytek.jzcpx.procuracy.cont.common.util.OcrResourcesUtil;
import com.iflytek.jzcpx.procuracy.cont.common.util.UUIDUtil;
import com.iflytek.jzcpx.procuracy.cont.model.ACDto;
import com.iflytek.jzcpx.procuracy.cont.model.ArchiveCatalogueResultDto;
import com.iflytek.jzcpx.procuracy.cont.model.Catalog;
import com.iflytek.jzcpx.procuracy.cont.model.CatalogInfoDto;
import com.iflytek.jzcpx.procuracy.cont.model.Cell;
import com.iflytek.jzcpx.procuracy.cont.model.CellData;
import com.iflytek.jzcpx.procuracy.cont.model.ContCatalogResult1;
import com.iflytek.jzcpx.procuracy.cont.model.ContObjModel;
import com.iflytek.jzcpx.procuracy.cont.model.JZML;
import com.iflytek.jzcpx.procuracy.cont.model.JZMLWJ;
import com.iflytek.jzcpx.procuracy.cont.model.OcrResult;
import com.iflytek.jzcpx.procuracy.cont.model.Page;
import com.iflytek.jzcpx.procuracy.cont.model.Table;
import com.iflytek.jzcpx.procuracy.cont.model.TaskResult;
import com.iflytek.jzcpx.procuracy.cont.model.Textline;
import com.iflytek.jzcpx.procuracy.cont.service.ContService;
import com.iflytek.jzcpx.procuracy.cont.vo.HandContModel;
import com.iflytek.jzcpx.procuracy.ocr.common.helper.SwxRequestHelper;
import com.iflytek.jzcpx.procuracy.ocr.entity.OcrFile;
import com.iflytek.jzcpx.procuracy.web.cont.bd.ContBDService;
import com.iflytek.jzcpx.procuracy.web.ocr.bd.OcrBDService;
import com.iflytek.sxs.dfs.client.FdfsClient;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

/**
 * @author iFLYREC
 */
@Service
public class ContBDServiceImpl implements ContBDService {

    @Autowired
    private OcrBDService ocrBDService;

    @Resource(name = "dzjzOcrContExecutor")
    private ThreadPoolTaskExecutor DZJZ_OCR_CONT_THREAD_POOL;

    private final static Logger logger = LoggerFactory.getLogger(ContBDServiceImpl.class);

    @Autowired
    private ContService contService;

    @Resource
    private RestTemplate restTemplate;

    @Autowired
    private FdfsClient fdfsClient;

    private final static String MATCH_REGEX = ".*(目录).*";

    private final static String NOT_RECOGNIZE_FILE = "未识别材料";

    @Value("${skynet.cont.path}")
    private String cont_engine_api;

    @Value("${skynet.cont.server}")
    private String cont_engne_server;

    @Override
    public List<OcrFile> getOcrByBSBH(String bsbh) {

        return ocrBDService.getOcrByBSBH(bsbh);
    }

    @Override
    public void matchResult(List<JZMLWJ> wjs, String bsbh) {
        List<OcrFile> fileList = getOcrByBSBH(bsbh);

        if (fileList != null && fileList.size() > 0) {
            // 如果存在，则匹配上识别结果
            wjs.forEach(ov -> {

                fileList.forEach(vv -> {
                    String xh = ov.getWJXH();
                    if (xh.toString().equals(vv.getWjxh())) {
                        // ov.setOcrRes(vv.getEngineResultPath());
                        ov.setFdfsEngineResultPath(vv.getEngineResultPath());
                    }
                });
            });
        }
    }

    /**
     * 编目
     *
     * @param mlList
     * @param wjList
     * @param BSBH
     * @return
     * @throws Exception
     */
    @Override
    public ArchiveCatalogueResultDto ocrAndContFile(List<JZML> mlList, List<JZMLWJ> wjList, String BSBH,JZML leafJzml)
            throws Exception {
        logger.info("图像识别并编目, BSBH: {}, mlSize: {}, wjSize: {}", BSBH, CollectionUtils.size(mlList),
                    CollectionUtils.size(wjList));
        if (mlList == null) {
            mlList = new ArrayList<JZML>();
        }
        String dir = "";
        ArchiveCatalogueResultDto dto = new ArchiveCatalogueResultDto();

        if (CollectionUtils.isNotEmpty(wjList)) {
            final CountDownLatch cdl = new CountDownLatch(wjList.size());
            wjList.forEach(vo -> {
                try {
                    if (StringUtils.isNotBlank(vo.getWJXH())) {
                        DZJZ_OCR_CONT_THREAD_POOL.execute(() -> {
                            String result = "{}";
                            try {
                                // 不再从共享磁盘读取文件，换用接口的方式获取文件. 如果识别结果在数据库里已经存储了，则从存储路径里获取
                                if (vo.getFdfsEngineResultPath() != null) {
                                    //读取fdfs文件内容
                                    ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
                                    int size = (int) fdfsClient.length(vo.getFdfsEngineResultPath());
                                    fdfsClient.downloadFile(outputStream, vo.getFdfsEngineResultPath());
                                    result = outputStream.toString("UTF-8");
                                    outputStream.close();
                                    outputStream = null;
                                } else {
                                    if (StringUtils.isNotBlank(vo.getOcrRes())) {
                                        result = vo.getOcrRes();
                                    } else {
                                        // 从接口里获取文件，进行ocr识别，并將识别结果存储下来，不存也不影响业务流程
                                        result = recognize(BSBH, vo.getWJXH());
                                    }
                                }
                            } catch (Exception e) {
                                e.printStackTrace();
                                logger.error("获取文件,调用OCR失败", e);
                            }
                            // 图片识别，处理完
                            if (StringUtils.isBlank(result)) {
                                logger.error("wjxh:{}图像识别结果为空！！！！！！！", vo.getWJXH());
                            }
                            vo.setOcrRes(result);
                            cdl.countDown();
                        });
                    } else {
                        logger.info(String.format("标识编码:%s,对应的文件编号为空%s", BSBH, JSONObject.toJSONString(vo)));
                        cdl.countDown();
                    }
                } catch (Exception e) {
                    logger.error(String.format("标识编码:%s,调用OCR服务失败!", BSBH), e);
                    cdl.countDown();
                }
            });
            cdl.await();
        }

        // 根据目录编号分组，是干嘛滴

        wjList.forEach(vo -> {
            if (StringUtils.isEmpty(vo.getMLBH())) {
                vo.setMLBH("1");
            }
            if (StringUtils.isEmpty(vo.getWJMC())) {
                vo.setWJMC(vo.getWJXSMC());
            }
        });

        ContObjModel contModel = new ContObjModel();

        //1.先排序，以window名称排序规则
        wjList = ContUtil.listDoOrder(wjList);

        //引擎编目的结果
        List<Catalog> catalog = catalogEngines(BSBH, wjList).getCatalogInfo().getCatalog();
/*
        //分解模块1分组逻辑删除-------------------------------------------------------------
        //人工加编目结果拆分编目对象
         ContUtil.contModelFormat(contModel, catalog, wjList);

        //重新获取了新的顺序
        wjList = contModel.getFileList();

        //文件重排序后，重新编目
         catalog = catalogEngines(BSBH, wjList).getCatalogInfo().getCatalog();
        //分解模块1分组逻辑删除结束-----------------------------------------------------------------------
*/

        List<CellData> cellList = new ArrayList<>();

        /*
        //没有目录
        if (contModel.getMulu().size() > 0) {

            try {
                //找到了目录，用目录取表格
                cellList = getCellData(contModel.getMulu(), contModel);
            } catch (Exception ex) {
                cellList = new ArrayList<>();

                logger.info("获取目录后，存在不合法的识别数据。走引擎编目结果");
            }
        } else {

        }
        */

        JZML jzml = new JZML();
        catalogByCellAndEngine(catalog, cellList, jzml, wjList, dto, contModel,leafJzml);
        return dto;
    }

    /**
     * 根据卷内目录文件和编目引擎结果编目
     *
     * @param catalogList
     * @param cellList
     * @param jzml
     * @param fileList
     * @param dto
     * @param contModel
     */
    private void catalogByCellAndEngine(List<Catalog> catalogList, List<CellData> cellList, JZML jzml,
                                        List<JZMLWJ> fileList, ArchiveCatalogueResultDto dto, ContObjModel contModel,JZML leafJzml) {
        List<JZML> jzmlList = dto.getJZML();
        if (CollectionUtils.isEmpty(jzmlList)) {
            jzmlList = new ArrayList<>();
        }
        List<JZMLWJ> jzmlwj = dto.getJZMLWJ();
        if (CollectionUtils.isEmpty(jzmlwj)) {
            jzmlwj = new ArrayList<>();
        }
        String jzbh = "";
        if (CollectionUtils.isEmpty(fileList)) {
            jzbh = fileList.get(0).getJZBH();
        }
        String mlbhRoot = "";
        if (jzml != null && StringUtils.isNotBlank(jzml.getMLBH())) {
            mlbhRoot = jzml.getMLBH();
            jzmlList.add(jzml);
        }

        //开始编目
        List<JZML> mlList = new ArrayList<>();
        int num = 1;
        if (!CollectionUtils.isEmpty(cellList)) {

            //目录合法性检查，如果目录不合肥，放弃用目录编排
            boolean heath = ContUtil.check(cellList, contModel.getItems());
            //取消目录编目路径
            heath = false;
            if (heath) {
                cont(catalogList, cellList, fileList, contModel, mlList);
            } else {
                logger.info("获取目录后，存在不合法的识别数据。走引擎编目结果");
            }
        }

        if (mlList != null && mlList.size() < 1) {
            logger.info(String.format("获取到的卷内目录文件表格信息为空!返回所有证据材料的CONT编目结果!"));
            mlList = handlerCellAndCatalog(catalogList, fileList, mlbhRoot, jzbh, num, 0, fileList.size(),leafJzml);
            if(null != leafJzml) {
            	mlList.forEach(ml -> { 
    				ml.setFMLBH(leafJzml.getMLBH()); 
    			});
    			mlList.add(leafJzml);
            }
          }

        //结束编目
        jzmlwj.addAll(fileList);
        dto.setJZMLWJ(jzmlwj);
        jzmlList.addAll(mlList);
        dto.setJZML(jzmlList);
        fileList.forEach(wj -> {
            wj.setOcrRes(null);
            wj.setFile(null);
        });
    }

    /**
     * @param file
     * @param params
     * @param trackId @return
     */
    @Override
    public String recognize(File file, JSONObject params, String trackId) {
        return contService.recognize(file, params, trackId);
    }

    /**
     * 下载任务图片到本地分析，此方法仅用于分析数据用
     *
     * @param bsbh
     * @param path
     */
    @Override
    public void downloadTaskFileFromSwx(String bsbh, String path) {

        String result = contService.getDZJZInfo(bsbh);
        logger.info(String.format("请求获取电子卷宗案件目录文件信息,返回数据:%s", result));
        JSONObject jsonObject = JSONObject.parseObject(result);

        if (jsonObject == null) {
            //获取目录文件失败
            logger.info("获取目录文件失败");
        }
        String jzml = "";
        String jzmlwj = "";
        if (jsonObject.getString("code").equals("0")) {
            String data = jsonObject.getString("data");
            JSONObject json = JSONObject.parseObject(data);
            jzml = json.getString("jzml");
            jzmlwj = json.getString("jzmlwj");
        }
        JSONArray jajzml = JSONArray.parseArray(jzml);
        JSONArray jajzmlwj = JSONArray.parseArray(jzmlwj);

        List<JZML> mlList = null;
        if (jajzml != null) {
            mlList = JSONObject.parseArray(jajzml.toJSONString(), JZML.class);
        }

        List<JZMLWJ> wjList = JSONObject.parseArray(jajzmlwj.toJSONString(), JZMLWJ.class);

        for (JZMLWJ jzmlwj1 : wjList) {
            String fileUrl = path + "\\" + jzmlwj1.getWJMC() + ".jpg";
            byte[] fileDatas = ocrBDService.getDZJZJPG(bsbh, bsbh, jzmlwj1.getWJXH());


            try {
                File file = new File(fileUrl);
                FileUtils.writeByteArrayToFile(file, fileDatas);
            } catch (Exception ex) {

            }
        }
    }

    /**
     * @param handContModel
     * @return
     */
    @Override
    public HandContResult handCont(HandContModel handContModel) {
        HandContResultInfo handContResultInfo = contService.getContDataFromSwx(handContModel.getJzbh(), handContModel.getMlbh());

        logger.info("获取智能编目文件列表：" + JSON.toJSONString(handContResultInfo));
        String bsbh = "";//标识编号
        HandContResult successJsonResult = new HandContResult();
        String zjbh = handContModel.getJzbh();
        if (handContResultInfo == null) {

            successJsonResult.setCode("404");
            successJsonResult.setMessage("无法加载智能编目服务");
            return successJsonResult;
        }
        //根据编目目录，取文件进行编目
        Iterator iterator = handContResultInfo.getJzml().iterator();
        List<ArchiveCatalogueResultDto> dtos = new ArrayList<>();
        ArchiveCatalogueResultDto archiveCatalogueResultDto = null;

        HandContResultInfoJzml mHHandContResultInfoJzml = new HandContResultInfoJzml();

        //找最高层目录
        while (iterator.hasNext()) {

            //从目录里找到文件，然后去编目,找一个目录
            HandContResultInfoJzml handContResultInfoJzml = (HandContResultInfoJzml) iterator.next();
            if (!StringUtils.isEmpty(handContResultInfoJzml.getTaskid())) {
                bsbh = handContResultInfoJzml.getTaskid();
            }
            if (StringUtils.isEmpty(handContResultInfoJzml.getFmlbh())) {
                mHHandContResultInfoJzml = handContResultInfoJzml;
            }
        }

        //目录编号
        final String mlbh = mHHandContResultInfoJzml.getMlbh();

        if (handContResultInfo.getJzmlwj() == null) {
            handContResultInfo.setJzml(new ArrayList<>());
        }
        //将所有的文件放到最高层目录下，进行编目
        handContResultInfo.getJzmlwj().forEach(c -> {
            c.setMlbh(mlbh);
        });

        //目录信息
        List<JZML> jzmls = contService.handContJzml2Jzml(mHHandContResultInfoJzml);
        
        JZML leafJzml=null;
        if(CollectionUtils.isNotEmpty(jzmls)) {
        	//查找叶子目录
        	jzmls.sort(Comparator.comparing(JZML::getMLSXH));
        	leafJzml=jzmls.get(jzmls.size()-1);
        }

        //根据目录筛选文件
        List<HandContResultInfoJzmlwj> handContResultInfoJzmlwjs = handContResultInfo.getJzmlwj();

        //卷宗文件
        List<JZMLWJ> jzmlwjs = contService.HandContJzmlwj(handContResultInfoJzmlwjs);


        bsbh = mHHandContResultInfoJzml.getTaskid();

        try {
            archiveCatalogueResultDto = this.ocrAndContFile(jzmls, jzmlwjs, bsbh,leafJzml);

            archiveCatalogueResultDto.setBSBH(jzmlwjs.get(0).getJZBH());

            //设置卷宗编号
            archiveCatalogueResultDto.getJZML().forEach(vo -> {
                vo.setJZBH(zjbh);
            });
        } catch (Exception ex) {
            logger.warn("编目异常", ex);
        }

        //合并编目信息
        logger.info(String.format("提交电子卷宗编目结果传入参数:%s", JSONObject.toJSONString(archiveCatalogueResultDto)));
        String resultSave = contService.saveDzjzzzInfo(JSONObject.toJSONString(archiveCatalogueResultDto));
        logger.info(String.format("提交电子卷宗编目结果返回参数: %s", resultSave));

        successJsonResult.setCode("00001");
        successJsonResult.setMessage("Success");
        successJsonResult.setSuccess(true);
        return successJsonResult;
    }

    /**
     * 结合单元格和编目结果进行编目
     *
     * @param catalogList
     * @param cellDataList
     * @param fileList
     * @param contModel
     * @param jzmls
     */
    private void cont(List<Catalog> catalogList, List<CellData> cellDataList, List<JZMLWJ> fileList, ContObjModel contModel, List<JZML> jzmls) {

        int num = 1;
        int fmNum = contModel.getFm().size();
        int muluNum = contModel.getMulu().size();
        int contentNum = contModel.getItems().size();


        //1先编封面
        if (contModel.getFm().size() > 0) {
            String fmName = ContUtil.getFmName(catalogList);
            JZML fmMu = new JZML();
            fmMu.setFMLBH("");
            fmMu.setMLLX(2);
            fmMu.setJZBH("");
            fmMu.setMLSXH(num++);
            fmMu.setMLBH(UUIDUtil.getuuid());
            fmMu.setMLXSMC(fmName);
            fmMu.setMLXX(fmName);
            jzmls.add(fmMu);

            for (int i = 0; i < contModel.getFm().size(); i++) {
                fileList.get(i).setMLBH(fmMu.getMLBH());
            }
        }
        //2再目录
        if (contModel.getMulu().size() > 0) {
            //将目录放到同一个目录下
            String nuluName = ContUtil.getMuluName(catalogList);

            JZML mulu = new JZML();
            mulu.setFMLBH("");
            mulu.setMLLX(2);
            mulu.setJZBH("");
            mulu.setMLSXH(num++);
            mulu.setMLBH(UUIDUtil.getuuid());
            mulu.setMLXSMC(nuluName);
            mulu.setMLXX(nuluName);
            jzmls.add(mulu);

            //先编封面
            for (int i = 0; i < contModel.getMulu().size(); i++) {
                fileList.get(i + fmNum).setMLBH(mulu.getMLBH());
            }
        }

        if (contModel.getItems().size() > 0) {
            for (int i = 0; i < cellDataList.size(); i++) {
                //取出目录的开始页码和结束页码
                int startNum = cellDataList.get(i).getStartNum();
                int endNum = cellDataList.get(i).getEndNum();
                String title = cellDataList.get(i).getValue().toString();

                JZML contentML = new JZML();
                contentML.setFMLBH("");
                contentML.setMLLX(2);
                contentML.setJZBH("");
                contentML.setMLSXH(num++);
                contentML.setMLBH(UUIDUtil.getuuid());
                contentML.setMLXSMC(title);
                contentML.setMLXX(title);
                jzmls.add(contentML);

                //产生一个目录
                int offset = fmNum + muluNum;
                for (int j = startNum + offset - 1; j < endNum + offset; j++) {
                    fileList.get(j).setMLBH(contentML.getMLBH());
                }
            }
        }

        if (contModel.getFd().size() > 0) {
            String fdName = ContUtil.getFdName(catalogList);
            JZML fdMu = new JZML();
            fdMu.setFMLBH("");
            fdMu.setMLLX(2);
            fdMu.setJZBH("");
            fdMu.setMLSXH(num++);
            fdMu.setMLBH(UUIDUtil.getuuid());
            fdMu.setMLXSMC(fdName);
            fdMu.setMLXX(fdName);
            jzmls.add(fdMu);

            int offset = fmNum + muluNum + contentNum;
            for (int i = offset; i < contModel.getFd().size() + offset; i++) {
                fileList.get(i).setMLBH(fdMu.getMLBH());
            }
        }
    }

    /**
     * 根据目录编目
     *
     * @param catalogList
     * @param cellList
     * @param fileList
     * @param mlbhRoot
     * @param mlList
     * @param jzbh
     * @param num
     */
    private void contByCatalogCellData(List<Catalog> catalogList, List<CellData> cellList,
                                       List<JZMLWJ> fileList, String mlbhRoot, List<JZML> mlList, String jzbh, int num, ContObjModel contModel) {
        try {
            logger.info(String.format("获取到的卷内目录文件表格信息:%s", JSONObject.toJSONString(cellList)));
            for (int i = 0; i < cellList.size(); i++) {

                //取目录一行
                CellData cellData = cellList.get(i);

                List<JZML> mlListFirst = new ArrayList<>();

                if (i == 0) { //第一行
                    if (cellData.getStartNum() > 1) {
                        mlListFirst = handlerCellAndCatalog(catalogList, fileList, mlbhRoot, jzbh, num, 0,
                                cellData.getStartNum());
                    }
                } else if (i == cellList.size() - 1) {//最后一样
                    if (fileList.size() > cellData.getEndNum() && cellData.getEndNum() != 0) {
                        if (cellData.getEndNum() > fileList.size()) {
                            return;
                        }
                        mlListFirst = handlerCellAndCatalog(catalogList, fileList, mlbhRoot, jzbh, num,
                                cellData.getEndNum() + 1, fileList.size());
                    }
                } else { //中间行
                    if (cellData.getStartNum() > cellList.get(i - 1).getEndNum() + 1) {
                        if (cellData.getStartNum() > fileList.size()) {
                            return;
                        }
                        mlListFirst = handlerCellAndCatalog(catalogList, fileList, mlbhRoot, jzbh, num,
                                cellList.get(i - 1).getEndNum(), cellData.getStartNum() - 1);
                    }
                }


                if (cellData.getStartNum() != 0 && cellData.getEndNum() != 0
                        && StringUtils.isNotBlank(cellData.getValue().toString())) {
                    JZML ml = new JZML();
                    ml.setFMLBH(mlbhRoot);
                    ml.setMLLX(2);
                    ml.setJZBH(jzbh);
                    ml.setMLSXH(num++);
                    ml.setMLBH(UUIDUtil.getuuid());
                    ml.setMLXSMC(cellData.getValue().toString());
                    ml.setMLXX(cellData.getValue().toString());
                    mlList.add(ml);
                    if (cellData.getEndNum() > fileList.size()) {
                        cellData.setEndNum(fileList.size());
                    }
                    List<JZMLWJ> wjSub = fileList.subList(cellData.getStartNum() - 1, cellData.getEndNum());
                    String mlbh = "";
                    if (!CollectionUtils.isEmpty(wjSub)) {
                        mlbh = wjSub.get(0).getMLBH();
                        if (StringUtils.isNotBlank(mlbh)) {
                            for (int j = 0; j < mlList.size(); j++) {
                                if (mlbh.equals(mlList.get(j).getMLBH()) && mlList.get(j).getMLLX() != 1) {
                                    mlList.remove(j);
                                }
                            }
                        }
                    }
                    wjSub.forEach(wj -> {
                        wj.setMLBH(ml.getMLBH());
                    });
                } else if (cellData.getEndNum() != 0 && cellData.getStartNum() == 0 && i == 0) {
                    int start = findEffectiveStartNum(cellList, i);
                    mlListFirst = handlerCellAndCatalog(catalogList, fileList, mlbhRoot, jzbh, num, start,
                            cellData.getEndNum());
                } else if (cellData.getStartNum() != 0 && cellData.getEndNum() == 0) {
                    int end = findEffectiveEndNum(cellList, i);
                    if (end != -1) {
                        mlListFirst = handlerCellAndCatalog(catalogList, fileList, mlbhRoot, jzbh, num,
                                cellData.getStartNum() - 1, end - 1);
                    } else {
                        mlListFirst = handlerCellAndCatalog(catalogList, fileList, mlbhRoot, jzbh, num,
                                cellData.getStartNum() - 1, fileList.size());
                    }
                }


                mlList.addAll(mlListFirst);
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    private int findEffectiveEndNum(List<CellData> cellList, int end) {
        for (int i = end + 1; i < cellList.size(); i++) {
            if (cellList.get(i).getStartNum() != 0 && cellList.get(i).getEndNum() != 0) {
                return cellList.get(i).getStartNum();
            }
        }
        return -1;
    }

    private int findEffectiveStartNum(List<CellData> cellList, int start) {
        for (int i = 0; i < start; i++) {
            if (cellList.get(i).getStartNum() != 0 && cellList.get(i).getEndNum() != 0) {
                return cellList.get(i).getEndNum();
            }
        }
        return 0;
    }

    /**
     * 处理表格和编目结果
     *
     * @param catalogList
     * @param fileList
     * @param mlbhRoot
     * @param jzbh
     * @param num
     * @param startNum
     * @param endNum
     * @return
     */
    private List<JZML> handlerCellAndCatalog(List<Catalog> catalogList, List<JZMLWJ> fileList, String mlbhRoot,
                                             String jzbh, int num, int startNum, int endNum,JZML leafJzml) {

        try {
            for (int i = 0; i < fileList.size(); i++) {
                if (fileList.get(i).getWJMC().contains("封面")) {
                    catalogList.get(i).setTitle("封面");
                }
                if (fileList.get(i).getWJMC().contains("封底")) {
                    catalogList.get(i).setTitle("封底");
                }
            }
        } catch (Exception ex) {
        }

        List<JZML> list = new ArrayList<>();
        for (int i = startNum; i < endNum; i++) {
            if (i >= catalogList.size()) {
                return list;
            }
            Catalog catalog = catalogList.get(i);
            JZML ml = new JZML();
            ml.setFMLBH(mlbhRoot);
            ml.setMLLX(2);
            ml.setJZBH(jzbh);
            ml.setMLSXH(num++);
            ml.setMLBH(UUIDUtil.getuuid());
            if (StringUtils.isNotBlank(catalog.getTitle().toString())
                    && !catalog.getTitle().toString().equals("unknown")) {
                ml.setMLXSMC(catalog.getTitle().toString());
                ml.setMLXX(catalog.getTitle().toString());
            } else {
                ml.setMLXSMC(NOT_RECOGNIZE_FILE);
                ml.setMLXX(NOT_RECOGNIZE_FILE);
            }
            fileList.get(i).setMLBH(ml.getMLBH());
            list.add(ml);
        }

        //目录合并
        List<String> titleList = new ArrayList<String>();
        list.forEach(c -> {
            if (!titleList.contains(c.getMLXSMC())) {
                titleList.add(c.getMLXSMC());
            }
        });
        List<JZML> mergeJZMLList = new ArrayList<JZML>();
        //遍历集合，创建新的目录
        int indexNum = leafJzml.getMLSXH();
        for (int k = 0; k < titleList.size(); k++) {
        	indexNum=indexNum+1;
            String c = titleList.get(k);
            //
            JZML ml = new JZML();
            ml.setFMLBH(mlbhRoot);
            ml.setMLLX(2);
            ml.setJZBH(jzbh);
            ml.setMLSXH(indexNum);
            ml.setMLBH(UUIDUtil.getuuid());
            ml.setMLXSMC(c);
            ml.setMLXX(c);
            mergeJZMLList.add(ml);
        }

        //遍历文件，获取标题
        fileList.forEach(c -> {
            String mlbh = c.getMLBH();

            for (int j = 0; j < list.size(); j++) {
                if (list.get(j).getMLBH().equals(mlbh)) {
                    //找到目录后，获取到标题
                    String muluName = list.get(j).getMLXX();
                    //用目录名称找目录
                    mergeJZMLList.forEach(v -> {
                        if (v.getMLXX().equals(muluName)) {
                            c.setMLBH(v.getMLBH());
                        }
                    });
                    break;
                }
            }
        });

        return mergeJZMLList;
    }
    
    
	private List<JZML> handlerCellAndCatalog(List<Catalog> catalogList, List<JZMLWJ> fileList, String mlbhRoot,
			String jzbh, int num, int startNum, int endNum) {

		try {
			for (int i = 0; i < fileList.size(); i++) {
				if (fileList.get(i).getWJMC().contains("封面")) {
					catalogList.get(i).setTitle("封面");
				}
				if (fileList.get(i).getWJMC().contains("封底")) {
					catalogList.get(i).setTitle("封底");
				}
			}
		} catch (Exception ex) {
		}

		List<JZML> list = new ArrayList<>();
		for (int i = startNum; i < endNum; i++) {
			if (i >= catalogList.size()) {
				return list;
			}
			Catalog catalog = catalogList.get(i);
			JZML ml = new JZML();
			ml.setFMLBH(mlbhRoot);
			ml.setMLLX(2);
			ml.setJZBH(jzbh);
			ml.setMLSXH(num++);
			ml.setMLBH(UUIDUtil.getuuid());
			if (StringUtils.isNotBlank(catalog.getTitle().toString())
					&& !catalog.getTitle().toString().equals("unknown")) {
				ml.setMLXSMC(catalog.getTitle().toString());
				ml.setMLXX(catalog.getTitle().toString());
			} else {
				ml.setMLXSMC(NOT_RECOGNIZE_FILE);
				ml.setMLXX(NOT_RECOGNIZE_FILE);
			}
			fileList.get(i).setMLBH(ml.getMLBH());
			list.add(ml);
		}

        //目录合并
		List<String> titleList = new ArrayList<String>();
		list.forEach(c -> {
			if (!titleList.contains(c.getMLXSMC())) {
				titleList.add(c.getMLXSMC());
			}
		});
		List<JZML> mergeJZMLList = new ArrayList<JZML>();
        //遍历集合，创建新的目录
		int indexNum = 1;
		for (int k = 0; k < titleList.size(); k++) {
			String c = titleList.get(k);
            //
			JZML ml = new JZML();
			ml.setFMLBH(mlbhRoot);
			ml.setMLLX(2);
			ml.setJZBH(jzbh);
			ml.setMLSXH(indexNum++);
			ml.setMLBH(UUIDUtil.getuuid());
			ml.setMLXSMC(c);
			ml.setMLXX(c);
			mergeJZMLList.add(ml);
		}

        //遍历文件，获取标题
		fileList.forEach(c -> {
			String mlbh = c.getMLBH();

			for (int j = 0; j < list.size(); j++) {
				if (list.get(j).getMLBH().equals(mlbh)) {
                    //找到目录后，获取到标题
					String muluName = list.get(j).getMLXX();
                    //用目录名称找目录
					mergeJZMLList.forEach(v -> {
						if (v.getMLXX().equals(muluName)) {
							c.setMLBH(v.getMLBH());
						}
					});
					break;
				}
			}
		});

		return mergeJZMLList;
	}  

    /**
     * @param ocrStr
     * @param contObjModel
     */
    private void getContTitleLieAndYemaNum(String ocrStr, ContObjModel contObjModel) {
        OcrResult ocrResult = OcrResourcesUtil.getOcrResult(ocrStr);

        TaskResult taskResult = null;
        if (ocrResult == null || (taskResult = ocrResult.getTaskResult()) == null) {
            return;
        }

        for (Page page : taskResult.getPage()) {

            // 处理表格区域
            for (Table table : page.getTable()) {
                List<Cell> cellList = table.getCell();
                Map<Integer, List<Cell>> cellMap = cellList.stream()
                        .collect(Collectors.groupingBy(Cell::getRow));


                for (Integer entry : cellMap.keySet()) {
                    List<Cell> cells = cellMap.get(entry);
                    for (int i = 0; i < cells.size(); i++) {
                        Cell cell = cells.get(i);
                        if (CollectionUtils.isNotEmpty(cell.getTextline())
                                && CollectionUtils.isNotEmpty(cell.getTextline().get(0).getSent())
                                && StringUtils.isNotBlank(cell.getTextline().get(0).getSent().get(0).getValue())) {

                            String value = "";
                            if (cell.getTextline().size() == 1) {
                                value = cell.getTextline().get(0).getSent().get(0).getValue();
                                if (value.contains("页")) {
                                    contObjModel.setYema(i);
                                    continue;
                                }
                                if (value.contains("名") || value.contains("称") || value.contains("标") || value.contains("题")) {
                                    contObjModel.setTitleLie(i);
                                    continue;
                                }
                            } else if (cell.getTextline().size() > 1) {


                                //内容为多行时，拼接文本
                                int lineSize = cell.getTextline().size();
                                for (int j = lineSize - 1; j >= 0; j--) {
                                    Textline textline = cell.getTextline().get(j);
                                    value = value + textline.getSent().get(0).getValue();
                                }

                                if (value.contains("页")) {
                                    contObjModel.setYema(i);
                                    continue;
                                }
                                if (value.contains("名") || value.contains("称") || value.contains("标") || value.contains("题")) {
                                    contObjModel.setTitleLie(i);
                                    continue;
                                }
                            }
                        }
                    }
                    break;
                }
            }
        }
    }

    /**
     * 一列转成一个目录
     *
     * @param cells
     * @param contObjModel
     * @return
     */
    private CellData getOneCellData(List<Cell> cells, ContObjModel contObjModel) {

        CellData cellData = new CellData();

        Cell cellTitle = cells.get(contObjModel.getTitleLie());

        if (CollectionUtils.isNotEmpty(cellTitle.getTextline())
                && CollectionUtils.isNotEmpty(cellTitle.getTextline().get(0).getSent())
                && StringUtils.isNotBlank(cellTitle.getTextline().get(0).getSent().get(0).getValue())) {

            String value = "";
            for (int j = 0; j < cellTitle.getTextline().size(); j++) {
                value = value + cellTitle.getTextline().get(j).getSent().get(0).getValue();
            }

            cellData.setValue(value);
        } else {
            //取标题异常
        }

        Cell cellPage = cells.get(contObjModel.getYema());

        if (CollectionUtils.isNotEmpty(cellPage.getTextline())
                && CollectionUtils.isNotEmpty(cellPage.getTextline().get(0).getSent())
                && StringUtils.isNotBlank(cellPage.getTextline().get(0).getSent().get(0).getValue())) {
            String value = cellPage.getTextline().get(0).getSent().get(0).getValue();

            cellData.setPageNumStr(value);

            String[] numStr = value.split("-");
            if (numStr.length == 2) {
                cellData.setStartNum(Integer.parseInt(numStr[0]));
                cellData.setEndNum(Integer.parseInt(numStr[1]));
            } else {
                cellData.setStartNum(Integer.parseInt(numStr[0]));
            }
        } else {
            //页码为空的，扔掉
            return null;
        }

        return cellData;
    }

    /**
     * @param mulus
     * @return
     */
    private List<CellData> getCellData(List<JZMLWJ> mulus, ContObjModel contObjModel) {

        List<CellData> data = new ArrayList<>();

        List<String> ocrList = new ArrayList<>();
        for (int i = 0; i < mulus.size(); i++) {
            ocrList.add(mulus.get(i).getOcrRes());
        }

        if (CollectionUtils.isNotEmpty(ocrList)) {

            //用第一张的表格获取标题和页码所在列
            getContTitleLieAndYemaNum(ocrList.get(0), contObjModel);

            //遍历多页的表格
            for (int i = 0; i < ocrList.size(); i++) {
                String ocrStr = ocrList.get(i);
                OcrResult ocrResult = OcrResourcesUtil.getOcrResult(ocrStr);

                TaskResult taskResult = null;
                if (ocrResult == null || (taskResult = ocrResult.getTaskResult()) == null) {
                    return null;
                }

                for (Page page : taskResult.getPage()) {

                    // 处理表格区域
                    for (Table table : page.getTable()) {
                        List<Cell> cellList = table.getCell();
                        Map<Integer, List<Cell>> cellMap = cellList.stream()
                                .collect(Collectors.groupingBy(Cell::getRow));
                        for (Integer entry : cellMap.keySet()) {
                            List<Cell> cells = cellMap.get(entry);
                            if (entry == 0 && i == 0) {
                                continue;
                            }
                            CellData cellData = getOneCellData(cells, contObjModel);

                            if (cellData != null) {
                                data.add(cellData);
                            }
                        }
                    }
                }
            }
        }
        logger.info(String.format("获取到的表格信息:%s", JSONObject.toJSONString(data)));
        return data;
    }

    /**
     * 获取卷宗目录的表格信息
     */
    @Override
    public List<CellData> getCatalogCell(List<Catalog> catalog, List<JZMLWJ> list) {
        List<CellData> data = new ArrayList<>();
        if (CollectionUtils.isEmpty(catalog)) {
            return null;
        }
        // 前10张材料中,获取卷宗目录文件
        int num = catalog.size() > 10 ? 10 : catalog.size();
        List<String> ocrList = new ArrayList<>();
        for (int i = 0; i < num; i++) {
            Catalog vo = catalog.get(i);
            logger.info(String.format("编目结果:%s", JSONObject.toJSONString(vo)));
            if (StringUtils.isNotBlank(vo.getTitle().toString())) {
                // 添加文件标题包含目录两字的材料
                if (vo.getTitle().toString().matches(MATCH_REGEX)) {
                    if (StringUtils.isNotBlank(list.get(i).getOcrRes())) {
                        ocrList.add(list.get(i).getOcrRes());
                        for (int j = i + 1; j < num; j++) {
                            // 卷宗目录材料可能存在多页情况
                            if (StringUtils.isBlank(catalog.get(j).getTitle().toString())
                                    && (catalog.get(j).getTitle().toString().equals(vo.getTitle().toString()))) {
                                ocrList.add(list.get(j).getOcrRes());
                            } else {
                                break;
                            }
                        }
                        break;
                    }
                }
            }
        }
        if (CollectionUtils.isNotEmpty(ocrList)) {
            for (int i = 0; i < ocrList.size(); i++) {
                String ocrStr = ocrList.get(i);
                OcrResult ocrResult = OcrResourcesUtil.getOcrResult(ocrStr);
                TaskResult taskResult = null;
                if (ocrResult == null || (taskResult = ocrResult.getTaskResult()) == null) {
                    return null;
                }
                for (Page page : taskResult.getPage()) {
                    // 处理表格区域
                    for (Table table : page.getTable()) {
                        List<Cell> cellList = table.getCell();
                        Map<Integer, List<Cell>> cellMap = cellList.stream()
                                .collect(Collectors.groupingBy(Cell::getRow));
                        for (Integer entry : cellMap.keySet()) {
                            List<Cell> cells = cellMap.get(entry);
                            // 第一张材料的第一行是表头信息,不处理.
                            if (i == 0 && entry == 0) {
                                continue;
                            }

                            handleCellData(cells, data, entry, list.size());
                        }
                    }
                }
            }
        }
        logger.info(String.format("获取到的表格信息:%s", JSONObject.toJSONString(data)));
        return data;
    }

    /**
     * @param cells 目录的一行集合 ，从中找出标题和页号
     * @param data
     * @param row
     * @param size
     */

    private void handleCellData(List<Cell> cells, List<CellData> data, Integer row, int size) {
        CellData cellData = new CellData();
        for (int i = 0; i < cells.size(); i++) {
            Cell cell = cells.get(i);
            if (cell.getCol() == 1) {
                if (CollectionUtils.isNotEmpty(cell.getTextline())
                        && CollectionUtils.isNotEmpty(cell.getTextline().get(0).getSent())
                        && StringUtils.isNotBlank(cell.getTextline().get(0).getSent().get(0).getValue())) {
                    cellData.setValue(cell.getTextline().get(0).getSent().get(0).getValue());
                } else {
                    logger.info(String.format("获取表格第 二 列第 %d 行单元格卷内目录文件材料名称为空!!!", row));
                    return;
                }
            }
            if (cell.getCol() == 2) {
                if (CollectionUtils.isNotEmpty(cell.getTextline())
                        && CollectionUtils.isNotEmpty(cell.getTextline().get(0).getSent())
                        && StringUtils.isNotBlank(cell.getTextline().get(0).getSent().get(0).getValue())) {
                    String value = cell.getTextline().get(0).getSent().get(0).getValue();
                    try {
                        String[] numStr = value.split("-");
                        if (numStr.length == 2) {
                            cellData.setEndNum(Integer.parseInt(numStr[1]));
                        } else {
                            cellData.setEndNum(Integer.parseInt(numStr[0]));
                        }
                        cellData.setStartNum(Integer.parseInt(numStr[0]));
                        if (CollectionUtils.isNotEmpty(data) && data.get(data.size() - 1).getEndNum() == 0) {
                            int startNum = Integer.parseInt(numStr[0]);
                            logger.info(
                                    String.format("根据当前单元格的起始页码: %d ,自动补全上个为空单元格的结束页码: %d .", startNum, startNum - 1));
                            data.get(data.size() - 1).setEndNum(startNum - 1);
                        }
                        if (cellData.getStartNum() > size || cellData.getEndNum() > size) {
                            return;
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                        logger.error(String.format("页码转换异常!对应值:%s", value), e);
                    }
                    cellData.setPageNumStr(value);
                } else {
                    logger.info(String.format("获取表格第 三 列第 %d 行单元格卷内目录文件页码为空!!!", row));
                    if (CollectionUtils.isNotEmpty(data) && data.get(data.size() - 1).getEndNum() != 0) {
                        int endNum = data.get(data.size() - 1).getEndNum();
                        if (endNum < size) {
                            logger.info(String.format("根据上个单元格的结束页码: %d ,自动补全当前为空单元格的起始页码: %d .", endNum, endNum + 1));
                            cellData.setStartNum(endNum + 1);
                        }
                    }
                }
            }
        }
        data.add(cellData);
    }

    /**
     * 编目引擎的结果
     *
     * @param bsbh
     * @param files
     * @return
     */
    @Override
    public CatalogInfoDto catalogEngines(String bsbh, List<JZMLWJ> files) {
        Map<String, Object> contParam = new HashMap<>();
        Map<String, Object> param = new HashMap<>();
        List<ACDto> acList = Collections.synchronizedList(new ArrayList(files.size()));
        for (int i = 0; i < files.size(); i++) {
            JZMLWJ jzmlwj = files.get(i);
            String id = jzmlwj.getWJXH() + "_" + bsbh + "_" + jzmlwj.getWJMC() + "_" + jzmlwj.getWJSXH();
            if (StringUtils.isNotBlank(jzmlwj.getOcrRes())) {
                acList.add(new ACDto(id, jzmlwj.getWJSXH(), JSON.parseObject(jzmlwj.getOcrRes(), JSONObject.class)));
            } else {
                acList.add(new ACDto(id, jzmlwj.getWJSXH(), JSON.parseObject("{}", JSONObject.class)));
            }
        }
        logger.info("引擎图片数:{}", acList.size());

        //取消用文件顺序号排序，改为用文件名称windows排序规则
//        acList.sort(new Comparator<ACDto>() {
//            @Override
//            public int compare(ACDto o1, ACDto o2) {
//                return o1.getAcSort() >= o2.getAcSort() ? 1 : -1;
//            }
//        });
        contParam.put("ocr_list", acList);
        long start = System.currentTimeMillis();
        String ac_json;
        CatalogInfoDto cataloginfo;
        param.put("in_encode", "UTF8");
        param.put("mode", "overwrite");
        param.put("out_encode", "UTF8");
        contParam.put("trackId", bsbh + "");
        contParam.put("param", param);
        ac_json = JSON.toJSONString(contParam);
        if (contParam.get("ocr_list") == null || acList.isEmpty()) {
            logger.error("调用编目引擎参数为空：{},{}", acList, files.toString());
        }
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.parseMediaType("application/json; charset=UTF-8"));
        String url = cont_engne_server + cont_engine_api;

        String result = "";
        // 请求编目引擎处理
        try {
            logger.info("开始请求编目引擎, url: {}, paramSize: {}", url, StringUtils.length(ac_json));
            result = restTemplate.postForObject(url, new HttpEntity<>(ac_json, headers), String.class);
            logger.info("请求编目引擎结束, respSize: {}, 耗时: {}ms", StringUtils.length(result),
                        System.currentTimeMillis() - start);
        } catch (Exception ex) {
            logger.warn("请求编目引擎异常", ex);
        }

        JSONObject temp = JSONObject.parseObject(result);
        JSONObject tag_temp = JSONObject.parseObject((String) temp.get("tag"));
        temp.remove("tag");
        temp.put("tag", tag_temp);
        ContCatalogResult1 cont_result = JSON.parseObject(temp.toString(), ContCatalogResult1.class);
        if (0 == cont_result.getState().getOk()) {
            logger.error("调用自动编目发生异常！！！！异常信息：{}", cont_result);
            return null;
        }
        cataloginfo = cont_result.getTag();
        logger.info("dossierId:{}自动编目接口, 图片数{}", bsbh, files.size());
        return cataloginfo;
    }
    @Autowired
    private SwxRequestHelper swxRequestHelper;
    /**
     * 图像识别，去除最外层结构后的结果
     *
     * @param bsbh
     * @param wjxh
     * @return
     */
    String recognize(String bsbh, String wjxh) {
        //byte[] fileBytes = ocrBDService.getDZJZJPG(bsbh, bsbh, wjxh);
	byte[] fileBytes = swxRequestHelper.getDZJZWJPG(bsbh, wjxh).getData().getData();

        JSONObject ocrParams = new JSONObject();
        ocrParams.put("pagetype", "page");
        ocrParams.put("resultlevel", "3");
        //ocrParams.put("funclist", "ocr,er");
        ocrParams.put("funclist", "");
        Path file = null;
        UUID uuid = UUID.randomUUID();
        String fileName = uuid.toString();
        try {
            file = Files.createTempFile(fileName, ".jpg");
            FileUtils.writeByteArrayToFile(file.toFile(), fileBytes);

            // 应用退出时删除文件, 兜底
            file.toFile().deleteOnExit();

            String resultStr = contService.recognize(file.toFile(), ocrParams, fileName);
            return resultStr;

        } catch (Exception e) {
            e.printStackTrace();
            return null;
        } finally {
            if (file.toFile() != null) {
                // 删除临时文件
                FileUtils.deleteQuietly(file.toFile());
            }
        }
    }
}